/* This library is supplied as-is.  Should you find any issues
 * or additional suggestions please feel free to submit them
 * to the project home and we will try to get them.
 * 
 * Project Home: http://flowview.codeplex.com/
 *
 * Author: Suroot
 * Original Distribution: 2010-12-18
 *
 * Revisions
 * 1 - Added On-Click functionality to where if no movement
 *     occurred then we will pass the event to the underlying
 *     element.
 *
 */

//{{{1 FlowView
function FlowView(variable_name, canvas_id, width, height, offset) {

  //{{{2 Variables
  if(variable_name == null) {
    alert("Global variable name must be given!");
    throw "Global variable name must be given!";
  }
  if(canvas_id == null) {
    alert("Canvas ID must be given!");
    throw "Canvas ID must be given!";
  }

  this.canvas = document.getElementById(canvas_id);
  if(this.canvas == null) {
    alert("Canvas " + canvas_id + " does not exist");
    throw "Canvas " + canvas_id + " does not exist";
  }
  this.variable_name = variable_name;
  this.w = (width == null) ? this.canvas.style.width : width;
  this.h = (height == null) ? this.canvas.style.width : height;
  this.canvas.width = this.w;
  this.canvas.height = this.h;
  this.offset = (offset == null) ? 10 : offset;
  this.title = "Title Here";
  this.bg = null;
  this.border = null;
  this.borderwidth = null;

  this.running = 0;
  this.lastdraw = new Date().getTime();
  this.updatetime = 30;
  this.current_x = 0;
  this.movement = 0;
  this.drift = .9;
  this.mouse_position = null;
  this.ismoving = 0;
  this.init = 1;
  this.mouse_moved = 0;

  this.elements = new Array(); 
  this.elementwidth = 200;
  this.elementheight = this.h * .8;
  this.elementoffset = (this.h * .1);
  this.elementdistance = this.offset * 2;
  
  //}}}2

  //{{{2 start
  this.start = function () {
    this.running = 1;
    this.init = 1;
    setTimeout(this.variable_name + ".main()", this.updatetime);
  };
  //}}}2

  //{{{2 main
  this.main = function () {
    if(this.running == 0) {
      return;
    }
    var ctx = this.canvas.getContext('2d');
    if(this.update() || this.init != 0) {
      var curr = new Date().getTime();
      if(curr >= this.lastdraw + (this.updatetime * 2) || this.init != 0) {
        this.draw(ctx);
        this.lastdraw = curr;
        this.init = 0;
      }
    }
    setTimeout(this.variable_name + ".main()", this.updatetime);
  };
  //}}}2

  //{{{2 update
  this.update = function () {
    var ret = false;
    if(this.elements.length == 0) {
      // No elements to update
      return ret;
    }
    if(this.movement == 0 && this.ismoving == 0) {
      // Nothing needs to be updated.
      return ret;
    }
    if(this.ismoving == 0) {
      if((this.elements[this.elements.length - 1].x + this.elements[this.elements.length - 1].w) < 0) {
        this.movement = 30;
      }
      if(this.elements[0].x > (this.w - (this.offset * 2))) {
        this.movement = -30;
      }
      if(this.movement != 0) {
        this.movement *= this.drift;
        this.current_x += this.movement;
        if((this.movement > 0 && this.movement < .05) || (this.movement < 0 && this.movement > -.05)) {
          this.movement = 0;
        }
        ret = true;
      }
    }
    if(this.movement == 0 && ret == false) {
      return ret;
    }
    ret = true;
    for(var i = 0; i < this.elements.length; i++) {
      this.elements[i].update(i);
    }
    return ret;
  };
  //}}}2

  //{{{2 draw
  this.draw = function (ctx) {
    ctx.save();
    ctx.clearRect(0, 0, this.w, this.h);
    DrawRoundedFrame(ctx, 0, 0, 10, this.canvas.width, this.canvas.height, this.bg, this.borderwidth, this.border);

    ctx.save();
    ctx.font = "bold 24px Arial";
    var metrics = ctx.measureText(this.title);
    ctx.fillText(this.title, (this.w / 2) - (metrics.width / 2), this.offset + 24);
    ctx.restore();
    ctx.beginPath();
    ctx.rect(this.offset * 2, this.offset * 2, this.w - (this.offset * 4), this.h - (this.offset * 4));
    ctx.closePath();
    ctx.clip();
    for(var i = 0; i < this.elements.length; i++) {
      this.elements[i].draw(ctx);
    }
    ctx.restore();
  };
  //}}}2

  //{{{2 add
  this.add = function (ele) {
    this.init = 1;
    ele.parent = this;
    ele.y = this.elementoffset;
    ele.w = this.elementwidth;
    ele.h = this.elementheight;
    ele.update(this.elements.length);
    this.elements.push(ele);
  }
  //}}}2

  //{{{2 mousedown
  this.mousedown = function (event) {
    this.ismoving = 1;
    this.mouse_position = {"x": event.clientX, "y": event.clientY};
    this.movement = 0;
  };
  //}}}2

  //{{{2 mouseup
  this.mouseup = function (event) {
    this.ismoving = 0;
    this.mouse_position = null;
  }
  //}}}2

  //{{{2 mousemove
  this.mousemove = function (event) {
    if(this.ismoving == 0) {
      return;
    }
    this.mouse_moved = 1;
    var nx = event.clientX - this.mouse_position.x;
    this.movement = nx;
    this.current_x += nx;
    this.mouse_position = {"x": event.clientX, "y": event.clientY};
  }
  //}}}2

  //{{{2 mouseclick
  this.mouseclick = function (event) {
    if(this.mouse_moved == 1) {
      this.mouse_moved = 0;
      return;
    }
    var relational = {"x": event.clientX,"y": event.clientY};

    relational.x = event.clientX;
    relational.y = event.clientY;

    for(var ele = this.canvas; ele != null; ele = ele.offsetParent) {
      relational.x -= ele.offsetLeft; 
      relational.y -= ele.offsetTop;
    }

    for(var i = 0; i < this.elements.length; i++) {
      if(relational.x > (this.elements[i].x) && 
         relational.y > (this.elements[i].y) &&
         relational.x < (this.elements[i].x + this.elements[i].w) && 
         relational.y < (this.elements[i].y + this.elements[i].h)) {
         relational.x -= this.elements[i].x;
         relational.y -= this.elements[i].y;
         return this.elements[i].mouseclick(event, relational);
      }
    }
    this.mouse_moved = 0;
  };
  //}}}2

}
//}}}1

//{{{1 FlowViewElement
function FlowViewElement(title, innerwindowdrawable, clickcallback) {

  //{{{2 Variables
  this.x = 0;
  this.y = 0;
  this.w = 0;
  this.h = 0;
  this.parent = null;
  this.backgroundstyle = "rgb(220,220,220)";
  this.position = 0;

  this.title = (title == null) ? "Title Here" : title;
  this.offset = 0;
  this.innerwindow = {"x": 0, "y": 0, "w": 0, "h": 0, "bg": "rgb(255,255,255)", "border": "rgb(0,0,0)", "borderwidth": 1};
  this.innerwindowdrawable = innerwindowdrawable;

  this.clickcallback = clickcallback;
  //}}}2

  //{{{2 update
  this.update = function (position) {
    if(this.parent == null) {
      return false;
    }
    this.position = position;
    this.x = this.parent.current_x + ((this.w + this.parent.elementdistance) * this.position);
    this.y = this.parent.elementoffset;
    this.w = this.parent.elementwidth;
    this.h = this.parent.elementheight;
    this.offset = this.w * .1;
    this.innerwindow.x = this.x + this.offset;
    this.innerwindow.y = this.y + (this.offset * 2);
    this.innerwindow.w = this.w - (this.offset * 2);
    this.innerwindow.h = this.h / 2;
    this.innerwindow.bg = "rgb(255,255,255)";
    this.innerwindow.border = "rgb(0,0,0)";
    this.innerwindow.borderwidth = 1;
    this.innerwindow.offset = 5;
    if(this.innerwindowdrawable != null) {
      this.innerwindowdrawable.update(this.innerwindow.x + this.innerwindow.offset, this.innerwindow.y + this.innerwindow.offset, this.innerwindow.w - (this.innerwindow.offset * 2), this.innerwindow.h - (this.innerwindow.offset * 2));
    }
    return true;
  };
  //}}}2

  //{{{2 draw
  this.draw = function (ctx) {
    if(((this.x + this.w) < 0) || ((this.x > this.parent.w))) {
      // If we are off screen, we do not draw.
      return;
    }
    DrawRoundedFrame(ctx, this.x, this.y, 8, this.w, this.h, this.backgroundstyle);
    
    DrawRoundedFrame(ctx, this.innerwindow.x, this.innerwindow.y, 4, this.innerwindow.w, this.innerwindow.h, this.innerwindow.bg, this.innerwindow.borderwidth, this.innerwindow.border);

    ctx.save();
    ctx.font = "bold 16px Arial";
    ctx.fillStyle = "rgb(0,0,0)";
    var metrics = ctx.measureText(this.title);
    ctx.fillText(this.title, this.x + (this.w / 2) - (metrics.width / 2), this.y + this.offset + 12);
    ctx.restore();
    if(this.innerwindowdrawable != null) {
      this.innerwindowdrawable.draw(ctx);
    }
  };
  //}}}2

  //{{{2 setinnerwindowdrawable
  this.setinnerwindowdrawable = function (ele) {
    if(ele.draw == null) {
      alert("Variable MUST have a draw function");
      throw "Variable MUST have a draw function";
    }
    if(ele.update == null) {
      alert("Variable MUST have an update function");
      throw "Variable MUST have an update function";
    }
    this.innerwindowdrawable = ele;
  }
  //}}}2

  //{{{2 mouseclick
  this.mouseclick = function (event, relationalevent) {
    if(this.clickcallback != null) {
      this.clickcallback(event, relationalevent);
    }
  }
  //}}}2

  //{{{2 setonclick
  this.setonclick = function (func) {
    this.clickcallback = func;
  }
  //}}}2

}
//}}}1

//{{{1 ImageFlowViewInnerWindow
function ImageFlowViewInnerWindow(url, alt) {

  //{{{2 Variables
  if(url == null) {
    alert("URL cannot be null");
    throw "URL cannot be null";
  }

  this.x = 0;
  this.y = 0;
  this.w = 0;
  this.h = 0;
  this.url = url;
  this.img = document.createElement("img");
  this.img.alt = (alt == null) ? "" : alt;
  this.img.src = url;
  //}}}2

  //{{{2 update
  this.update = function (x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  };
  //}}}2

  //{{{2 draw
  this.draw = function (ctx) {
    ctx.save();
    ctx.drawImage(this.img, this.x, this.y, this.w, this.h);
    ctx.restore();
  };
  //}}}2

}
//}}}1

//{{{1 DrawRoundedFrame
/*
Positions
t_l - top left
t_r - top right
r_t - right top
r_b - right bottom
b_r - bottom right
b_l - bottom left
l_b - left bottom
l_t - left top

    t_l       t_r
     /---------\
l_t  |         |  r_t
     |         |
     |         |
     |         |
l_b  |         |  r_b
     \---------/
    b_l       b_r
*/

function DrawRoundedFrame(ctx, x, y, offset, w, h, bgstyle, border_width, borderstyle) {
  function Point(x, y) {
    this.x = x;
    this.y = y;
  };
  var inlay = offset * 2;

  var t_l = new Point(x + inlay, y + offset);
  var t_r = new Point(x + w - inlay, y + offset);
  var r_t = new Point(x + w - offset, y + inlay);
  var r_b = new Point(x + w - offset, y + h - inlay);
  var b_r = new Point(x + w - inlay, y + h - offset);
  var b_l = new Point(x + inlay, y + h - offset);
  var l_b = new Point(x + offset, y + h - inlay);
  var l_t = new Point(x + offset, y + inlay);

  // Save from everything else
  ctx.save();
  ctx.fillStyle = (bgstyle == null) ? "rgb(250,250,242)" : bgstyle;
  ctx.beginPath();
  ctx.moveTo(t_l.x, t_l.y);

  // Top
  ctx.lineTo(t_r.x, t_r.y);
  ctx.quadraticCurveTo(r_t.x, t_r.y, r_t.x, r_t.y);

  // Right
  ctx.lineTo(r_b.x, r_b.y);
  ctx.quadraticCurveTo(r_b.x, b_r.y, b_r.x, b_r.y);

  // Bottom
  ctx.lineTo(b_l.x, b_l.y);
  ctx.quadraticCurveTo(l_b.x, b_l.y, l_b.x, l_b.y);

  // Left
  ctx.lineTo(l_t.x, l_t.y);
  ctx.quadraticCurveTo(l_t.x, t_l.y, t_l.x, t_l.y);

  ctx.fill();
  ctx.closePath();
  ctx.strokeStyle = (borderstyle == null) ? "rgb(153,204,255)" : borderstyle;
  ctx.beginPath();
  ctx.lineWidth = (border_width == null) ? 2 : border_width;
  ctx.moveTo(t_l.x, t_l.y);

  // Top
  ctx.lineTo(t_r.x, t_r.y);
  ctx.quadraticCurveTo(r_t.x, t_r.y, r_t.x, r_t.y);

  // Right
  ctx.lineTo(r_b.x, r_b.y);
  ctx.quadraticCurveTo(r_b.x, b_r.y, b_r.x, b_r.y);

  // Bottom
  ctx.lineTo(b_l.x, b_l.y);
  ctx.quadraticCurveTo(l_b.x, b_l.y, l_b.x, l_b.y);

  // Left
  ctx.lineTo(l_t.x, l_t.y);
  ctx.quadraticCurveTo(l_t.x, t_l.y, t_l.x, t_l.y);

  ctx.closePath();
  ctx.stroke();
  ctx.restore();
}
//}}}1

//{{{1 InitFlowView
function InitFlowView(var_name, canvas_name, width, height) {
  self[var_name] = new FlowView(var_name, canvas_name, width, height);

  self[var_name].canvas.onmousedown = function (event) { return self[var_name].mousedown(event); };
  self[var_name].canvas.onmouseup = function (event) { return self[var_name].mouseup(event); };
  self[var_name].canvas.onmousemove = function (event) { return self[var_name].mousemove(event); };
  self[var_name].canvas.onclick = function (event) { return self[var_name].mouseclick(event); };
  return self[var_name];
}
//}}}1
